package concurrency;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 什么时候使用显示的Lock
 *
 * @author fireway
 * @since 2019年 01月 07日 星期一 00:10:16 CST
 */
public class AttemptLocking {
    private ReentrantLock mLock = new ReentrantLock();

    // No optimization
    // 变量mVD是volatile修饰的，以努力确保不进行任何编译器优化
    private volatile double mVD;

    private void timed() {
        boolean captured = false;

        try {
            captured = mLock.tryLock(2, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        try {
            System.out.println("timed() " + mLock + " call tryLock(long time, TimeUnit unit): " + captured);
            longRunningOperator();
        } finally {
            if (captured) {
                mLock.unlock();
            }
        }
    }

    private void untimed() {
        boolean captured = mLock.tryLock();
        try {
            System.out.println("untimed() " + mLock + " call tryLock(): " + captured);
            longRunningOperator();
        } finally {
            if (captured) {
                mLock.unlock();
            }
        }
    }

    /**
     * An expensive, interruptable operation:
     * 执行XXX次开销相当大的浮点运算, 开平方运算，包括double类型的加法与除法
     * 有了这些运算就能看到优先级为MAX_PRIORITY的线程被线程调度器
     * 优先选择
     */
    private void longRunningOperator() {
        long startTime = System.nanoTime();
        for (int k = 0; k < 100; k++) {
            for (int j = 0; j < 100; j++) {
                for(int i = 1; i < 10000; i++) {
                    mVD = (Math.PI + Math.E) / (double)i;
                    mVD = mVD * mVD * mVD + (Math.PI + Math.E);
                    mVD = Math.sqrt(mVD);
                    mVD = Math.sqrt(mVD);
                    mVD = Math.sqrt(mVD);
                    mVD = Math.sqrt(mVD);
                    if(i % 1000 == 0) {
                        Thread.yield();
                    }
                }
            }
        }
        long duration = System.nanoTime() - startTime;
        System.out.println("longRunningOperator duration[" + duration + "]");
    }

    public static void main(String[] args) {
        final AttemptLocking al = new AttemptLocking();

        al.untimed();  // True -- lock is available
        al.timed();  // True -- lock is available

        // Now create a separate task to grab the lock:
        new Thread() {
            {
                setPriority(MAX_PRIORITY);
                setDaemon(true);
            }

            @Override
            public void run() {
                al.mLock.lock();
                System.out.println("run(): " + al.mLock + " acquired");
            }
        } .start();

        // Thread.yield();  // Give the 2nd task a chance

        // 上面的yield只是线程调度器的一个让步建议，实际测试很难出现。所以先暂时使用
        // sleep方式来时main线程进入睡眠状态。这样后面的主线程再想去拿锁的时候就会失败。
        // 本例子主要考察的侧重点是，能够获取tryLock()的返回值，而用synchronized关键字没有返回值
        try {
            TimeUnit.MILLISECONDS.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        al.untimed();  // False -- lock grabbed by task
        al.timed();  // False -- lock grabbed by task
    }
}
